# Copyright (c) 2019 Presto Labs Pte. Ltd.
# Author: leon

import datetime
import ujson as json
import functools

from coin.base.book.types import TradeSide, create_market_trade_event
from coin.exchange.base.ws.handler import WebsocketHandler
from coin.exchange.deribit_v1.kr_rest.futures_product import DeribitFuturesProduct
from coin.exchange.deribit_v2.book_builder import BookBuilder


@functools.lru_cache(maxsize=64)
def _get_product_from_instrument_name_impl(instrument_name, as_of_timestamp):
  current_time = datetime.datetime.fromtimestamp(as_of_timestamp / 1.e9)
  return DeribitFuturesProduct.FromStrNativeProduct(instrument_name, current_time)


def get_product_from_instrument_name(instrument_name, timestamp):
  as_of_timestamp = (timestamp // (1800 * 10**9)) * (1800 * 10**9)
  return _get_product_from_instrument_name_impl(instrument_name, as_of_timestamp)


def to_trade_side(side):
  if side == 'buy':
    return TradeSide.TRADE_BUY_SIDE
  elif side == 'sell':
    return TradeSide.TRADE_SELL_SIDE
  else:
    raise ValueError(side)


class DeribitHandler(WebsocketHandler):
  def on_book(self, record, queue_data, topic_data):
    message = record.data
    if not isinstance(message, dict):
      message = json.loads(message.tobytes())
    data = message['params']['data']
    native_symbol = data['instrument_name']
    product = get_product_from_instrument_name(native_symbol, record.timestamp)
    if not self.is_subscribed('book', native_symbol):
      return
    bids = data['bids']
    asks = data['asks']

    if 'prev_change_id' not in data:
      # First full book case.
      book = BookBuilder(native_symbol, book_builder_observer=self._book_builder_observer)
      self._books[native_symbol] = book
      book.snapshot(bids, asks)
      self.ready = True
    else:
      # Afterwards update case.
      if not self.ready or native_symbol not in self._books:
        return
      book = self._books[native_symbol]
      book.update(bids, asks)

    book.update_timestamp(record.timestamp)
    book.update_topic_id(record.topic_id)

    if self._feed_checker:
      self._feed_checker.on_book(product=product, book=book, timestamp=record.timestamp)
    self.publish_event('book', native_symbol, book)

  def on_trade(self, record, queue_data, topic_data):
    message = record.data
    if not isinstance(message, dict):
      message = json.loads(message.tobytes())

    data = message['params']['data']
    native_symbol = data[0]['instrument_name']
    product = get_product_from_instrument_name(native_symbol, record.timestamp)
    if not self.is_subscribed('trade', native_symbol):
      return
    for item in data:
      price = float(item['price'])
      qty = float(item['amount'])
      side = to_trade_side(item['direction'])
      trade_event = create_market_trade_event(record.timestamp,
                                              native_symbol,
                                              price,
                                              qty,
                                              side,
                                              topic_id=record.topic_id)
      if self._feed_checker:
        self._feed_checker.on_trade(product=product,
                                    trade_event=trade_event,
                                    timestamp=record.timestamp)
      self.publish_event('trade', native_symbol, trade_event)
